Skip to content

Retrieve gRPC server.address/server.port from gRPC target#16161

Draft
trask wants to merge 11 commits into
open-telemetry:mainfrom
trask:grpc-server-address
Draft

Retrieve gRPC server.address/server.port from gRPC target#16161
trask wants to merge 11 commits into
open-telemetry:mainfrom
trask:grpc-server-address

Conversation

@trask

@trask trask commented Feb 12, 2026

Copy link
Copy Markdown
Member

Currently built on top of

Prototype for open-telemetry/semantic-conventions#3317

Java agent instrumentation can do this for all gRPC versions.

Library instrumentation can only do this (automatically) using an internal gRPC API available since version 1.64.0: InternalManagedChannelBuilder.

I'm not convinced about the API / dependency on internals, could limit us on marking it stable, but marking ready-for-review to get feedback.

Related to #15871

@lmolkova lmolkova left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great (assuming you're comfortable depending on '@internal public' API)

@trask trask force-pushed the grpc-server-address branch from c45ee61 to ce22789 Compare February 13, 2026 04:14
@trask trask marked this pull request as ready for review February 13, 2026 22:54
@trask trask requested a review from a team as a code owner February 13, 2026 22:54
@trask trask force-pushed the grpc-server-address branch from 9a1c3d7 to 2a0e0b9 Compare February 17, 2026 17:14
@trask trask force-pushed the grpc-server-address branch 5 times, most recently from 0f60636 to dd4d89f Compare February 26, 2026 04:09
@trask trask marked this pull request as draft February 26, 2026 04:28
@trask trask force-pushed the grpc-server-address branch 6 times, most recently from d93c7e4 to e19f9d2 Compare March 1, 2026 02:27
@trask trask force-pushed the grpc-server-address branch from e19f9d2 to 9ae10a5 Compare March 2, 2026 21:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for deriving server.address / server.port for gRPC client spans from the channel “target” string (when available), aligning with emerging RPC stable semantic conventions and improving accuracy beyond authority()-only parsing.

Changes:

  • Introduces internal parsing utilities (GrpcTargetParser / ParsedTarget) and wires them into client request attribute extraction.
  • Adds a new recommended library API GrpcTelemetry#addClientInterceptor(ManagedChannelBuilder) that (on gRPC 1.64+) captures the channel target via InternalManagedChannelBuilder.interceptWithTarget.
  • Updates javaagent + Armeria gRPC javaagent instrumentation to pass through target/authority where available (gated by stable RPC semconv opt-in).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParserTest.java Adds unit coverage for parsing various gRPC target formats into address/port.
instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java Updates tests to use the new addClientInterceptor API.
instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcStreamingTest.java Updates streaming tests to use the new addClientInterceptor API.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java Switches server request creation to GrpcRequest.createServerRequest.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java Plumbs an optional target through interceptor construction and uses it to enrich client request attributes.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/ParsedTarget.java Adds a small internal value type to carry parsed address/port.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/Internal.java Adds an internal bridge for javaagent/library coordination when creating interceptors with target info.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParser.java Implements parsing of gRPC target strings into address/port.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java Uses new GrpcRequest server address/port accessors for client network attributes.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetry.java Adds addClientInterceptor with reflective gRPC 1.64+ target capture; deprecates createClientInterceptor.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java Refactors request to store server address/port explicitly and adds client/server factory methods.
instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcNetworkServerAttributesGetter.java Uses new GrpcRequest server address/port accessors for server network attributes.
instrumentation/grpc-1.6/library/README.md Updates usage docs to recommend addClientInterceptor.
instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java Reworks client interceptor creation to allow passing a target string.
instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java Captures builder target (when stable semconv enabled) and creates a target-aware interceptor.
instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java Passes URI authority as the target (when stable semconv enabled) for target-aware interceptor creation.

trask added 3 commits May 11, 2026 19:21
# Conflicts:
#	instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java
#	instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java
#	instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java
#	instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcServerNetworkAttributesGetter.java
#	instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java
…ry proxy

Previously the InvocationHandler forwarded any non-newInterceptor call to the
ManagedChannelBuilder instance, which gave Object methods (equals/hashCode/
toString) misleading semantics — e.g. proxy.equals(builder) would return true.

Handle equals/hashCode/toString explicitly with identity semantics, and reject
any other unexpected method.
The GrpcRequest hostFromAuthority/portFromAuthority helpers split on the first
':', which mangles IPv6 authorities like '[::1]:8080' (host becomes '[' and the
port parse fails). This is now both the fallback path for clients on gRPC <
1.64 and the primary path for server-side requests.

Promote the bracket-aware parsing logic from GrpcTargetParser into a new
parseAuthority() helper and route both client fallback and server request
construction through it, so authority and target parsing share a single source
of truth.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

trask added 4 commits May 11, 2026 20:04
Previously parseHostPort("") returned a ParsedTarget with an empty
address, which could lead to emitting server.address="" on spans for
targets like "dns:" or "dns:///". Return null instead and propagate
through parseSingleColonScheme / parseDnsScheme.
Previously parseHostPort fell back to ParsedTarget(hostPort, null) when
the port failed to parse, causing server.address to contain a trailing
colon or invalid port fragment (e.g. "myhost:" or "myhost:abc").
Return ParsedTarget(host, null) instead so server.address holds just
the host.
createClientRequest and createServerRequest are only called from
TracingClientInterceptor / TracingServerInterceptor in the same
package. Avoid widening the public API surface (and avoid exposing
the internal ParsedTarget type via a public signature).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment on lines +59 to +63
int slashIndex = rest.indexOf('/');
if (slashIndex != -1) {
return new ParsedTarget(rest.substring(slashIndex), null);
}
return new ParsedTarget(rest, null);
Comment on lines +96 to +132
if (interceptWithTargetMethod != null && interceptorFactoryClass != null) {
try {
interceptWithTargetMethod.invoke(null, builder, newInterceptorFactory());
return;
} catch (Exception e) {
logger.log(FINE, "Failed to use interceptWithTarget, falling back", e);
}
}

// Fallback for gRPC < 1.64.0: add interceptor without target info
builder.intercept(newTracingClientInterceptor(null));
}

private Object newInterceptorFactory() {
// Proxies InternalManagedChannelBuilder$InternalInterceptorFactory, whose only declared
// method is newInterceptor(String target). Object methods get identity semantics — we have
// no natural delegate to forward them to.
return Proxy.newProxyInstance(
ManagedChannelBuilder.class.getClassLoader(),
new Class<?>[] {interceptorFactoryClass},
(proxy, method, args) -> {
if ("newInterceptor".equals(method.getName())) {
return newTracingClientInterceptor((String) args[0]);
}
switch (method.getName()) {
case "equals":
return proxy == args[0];
case "hashCode":
return System.identityHashCode(proxy);
case "toString":
return "GrpcInterceptorFactory";
default:
throw new UnsupportedOperationException(method.toString());
}
});
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants